﻿using System;
using System.Windows;
using System.Windows.Controls;

namespace Trade.Journal.Controls
{
    /// <summary>
    /// 按照步骤 1a 或 1b 操作，然后执行步骤 2 以在 XAML 文件中使用此自定义控件。
    ///
    /// 步骤 1a) 在当前项目中存在的 XAML 文件中使用该自定义控件。
    /// 将此 XmlNamespace 特性添加到要使用该特性的标记文件的根
    /// 元素中:
    ///
    ///     xmlns:MyNamespace="clr-namespace:Trade.Journal"
    ///
    ///
    /// 步骤 1b) 在其他项目中存在的 XAML 文件中使用该自定义控件。
    /// 将此 XmlNamespace 特性添加到要使用该特性的标记文件的根
    /// 元素中:
    ///
    ///     xmlns:MyNamespace="clr-namespace:Trade.Journal;assembly=Trade.Journal"
    ///
    /// 您还需要添加一个从 XAML 文件所在的项目到此项目的项目引用，
    /// 并重新生成以避免编译错误:
    ///
    ///     在解决方案资源管理器中右击目标项目，然后依次单击
    ///     “添加引用”->“项目”->[浏览查找并选择此项目]
    ///
    ///
    /// 步骤 2)
    /// 继续操作并在 XAML 文件中使用控件。
    ///
    ///     <MyNamespace:DateTimePicker/>
    ///
    /// </summary>
    public class DateTimePicker : Control
    {
        static DateTimePicker()
        {
            DefaultStyleKeyProperty.OverrideMetadata(typeof(DateTimePicker), new FrameworkPropertyMetadata(typeof(DateTimePicker)));
        }

        private DatePicker PART_Date;

        public override void OnApplyTemplate()
        {
            base.OnApplyTemplate();

            PART_Date = Template.FindName("PART_Date", this) as DatePicker;
            var btn1 = Template.FindName("PART_ButtonNow", this) as Button;
            if (btn1 != null)
                btn1.Click += Now_Click;

            var btn2 = Template.FindName("PART_ButtonReset", this) as Button;
            if (btn2 != null)
                btn2.Click += Reset_Click;

            if (PART_Date != null)
            {
                if (SelectedDateTime != default)
                    PART_Date.SelectedDate = SelectedDateTime.Date;
                PART_Date.SelectedDateChanged += (s, e) => { if (PART_Date.SelectedDate.HasValue) SelectedDateTime = PART_Date.SelectedDate.Value + new TimeSpan(Hour, Minute, Second); };
            }
        }

        private void Reset_Click(object sender, RoutedEventArgs e)
        {
            Hour = 0;
            Minute = 0;
            Second = 0;
        }

        private void Now_Click(object sender, RoutedEventArgs e)
        {
            SelectedDateTime = DateTime.Now;
        }

        public DateTime SelectedDateTime
        {
            get { return (DateTime)GetValue(SelectedDateTimeProperty); }
            set { SetValue(SelectedDateTimeProperty, value); }
        }

        // Using a DependencyProperty as the backing store for SelectedDateTime.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty SelectedDateTimeProperty =
            DependencyProperty.Register("SelectedDateTime", typeof(DateTime), typeof(DateTimePicker), new FrameworkPropertyMetadata(default(DateTime), FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, new PropertyChangedCallback(OnDateTime)));

        private static void OnDateTime(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            if (d is DateTimePicker p && e.NewValue is DateTime t)
            {
                if (p.PART_Date != null && (!p.PART_Date.SelectedDate.HasValue || p.PART_Date.SelectedDate.Value != t.Date))
                    p.PART_Date.SelectedDate = t.Date;

                if (p.Hour != t.Hour)
                    p.Hour = t.Hour;

                if (p.Minute != t.Minute)
                    p.Minute = t.Minute;

                if (p.Second != t.Second)
                    p.Second = t.Second;
            }
        }

        internal int Hour
        {
            get { return (int)GetValue(HourProperty); }
            set { SetValue(HourProperty, value); }
        }

        // Using a DependencyProperty as the backing store for Hour.  This enables animation, styling, binding, etc...
        internal static readonly DependencyProperty HourProperty =
            DependencyProperty.Register("Hour", typeof(int), typeof(DateTimePicker), new PropertyMetadata(0, new PropertyChangedCallback(OnTime), new CoerceValueCallback(OnHour)));

        private static void OnTime(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            if (d is DateTimePicker p && e.NewValue is DateTime t && p.PART_Date != null && p.PART_Date.SelectedDate.HasValue)
                p.SelectedDateTime = p.PART_Date.SelectedDate.Value + new TimeSpan(p.Hour, p.Minute, p.Second);
        }

        private static object OnHour(DependencyObject d, object baseValue)
        {
            if (d is DateTimePicker p && baseValue is int h)
                return Math.Max(0, Math.Min(h, 23));
            else
                return baseValue;
        }

        internal int Minute
        {
            get { return (int)GetValue(MinuteProperty); }
            set { SetValue(MinuteProperty, value); }
        }

        // Using a DependencyProperty as the backing store for Minute.  This enables animation, styling, binding, etc...
        internal static readonly DependencyProperty MinuteProperty =
            DependencyProperty.Register("Minute", typeof(int), typeof(DateTimePicker), new PropertyMetadata(0, new PropertyChangedCallback(OnTime), new CoerceValueCallback(OnMinute)));

        private static object OnMinute(DependencyObject d, object baseValue)
        {
            if (d is DateTimePicker p && baseValue is int h)
                return Math.Max(0, Math.Min(h, 59));
            else
                return baseValue;
        }

        internal int Second
        {
            get { return (int)GetValue(SecondProperty); }
            set { SetValue(SecondProperty, value); }
        }

        // Using a DependencyProperty as the backing store for Second.  This enables animation, styling, binding, etc...
        internal static readonly DependencyProperty SecondProperty =
            DependencyProperty.Register("Second", typeof(int), typeof(DateTimePicker), new PropertyMetadata(0, new PropertyChangedCallback(OnTime), new CoerceValueCallback(OnMinute)));




    }
}
